﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace EntityBinder.Json
{
	class JsonElement
	{
		public enum ElementType
		{
			DICTIONARY,
			VALUE,
			ARRAY,
			CONTAINER
		}

		private ElementType m_elementType;
		public ElementType Type
		{
			get { return m_elementType; }
		}

		private object m_value;

		protected JsonElement(ElementType _type, object _value)
		{
			m_elementType = _type;
			m_value = _value;
		}

		public JsonElement(string _value)
			: this(ElementType.VALUE, _value)
		{

		}

		public JsonElement(JsonElement _container)
			: this(ElementType.CONTAINER, _container)
		{

		}

		public JsonElement(IEnumerable<JsonElement> _array)
			: this(ElementType.ARRAY, _array)
		{

		}

		public JsonElement(IDictionary<string, JsonElement> _dictionary)
			: this(ElementType.DICTIONARY, _dictionary)
		{
			
		}

		public string GetAsValue()
		{

			if(IsValue())
			{
				Debug.Assert((m_value as string) != null);

				return m_value as string;
			}
			throw new EntityBinderException("JsonElement", "GetAsValue", "Can not get as value a non value element : " + m_elementType);
		}

		public JsonElement GetAsContainer()
		{
			if(IsContainer())
			{
				Debug.Assert((m_value as JsonElement) != null);

				return m_value as JsonElement;
			}
			throw new EntityBinderException("JsonElement", "GetAsContainer", "Can not get as container a non container element : " + m_elementType);
		}

		public IEnumerable<JsonElement> GetAsArray()
		{
			if(IsArray())
			{
				Debug.Assert((m_value as IEnumerable<JsonElement>) != null);

				return m_value as IEnumerable<JsonElement>;
			}
			throw new EntityBinderException("JsonElement", "GetAsArray", "Can not get as array a non array element : " + m_elementType);
		}

		public IDictionary<string, JsonElement> GetDictionary()
		{
			if(IsDictionary())
			{
				Debug.Assert((m_value as IDictionary<string, JsonElement>) != null);

				return m_value as IDictionary<string, JsonElement>;
			}
			throw new EntityBinderException("JsonElement", "GetKeys", "Can not get dictionary from a non dictionary element : " + m_elementType);
		}

		#region check type

		public bool IsValue()
		{
			return IsType(ElementType.VALUE);
		}

		public bool IsArray()
		{
			return IsType(ElementType.ARRAY);
		}

		public bool IsContainer()
		{
			return IsType(ElementType.CONTAINER);
		}

		public bool IsDictionary()
		{
			return IsType(ElementType.DICTIONARY);
		}

		public bool IsType(ElementType _type)
		{
			return this.Type == _type;
		}

		#endregion

		public static JsonElement ParseJson(string _jsonContent)
		{
			Dictionary<string, object> content = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(_jsonContent);

			return new JsonElement(new JsonElement(Process(content)));
		}

		private static IEnumerable<JsonElement> Parse(Newtonsoft.Json.Linq.JArray _array)
		{
			List<JsonElement> result = new List<JsonElement>();

			foreach(Newtonsoft.Json.Linq.JToken token in _array)
			{
				result.Add(Process(token));
			}

			return result;
		}

		private static JsonElement Parse(Newtonsoft.Json.Linq.JObject _object)
		{
			Dictionary<string, JsonElement> result = new Dictionary<string, JsonElement>();

			foreach(KeyValuePair<string, Newtonsoft.Json.Linq.JToken> d in _object)
			{
				result.Add(d.Key, Process(d.Value));
			}

			return new JsonElement(result);
		}

		private static Dictionary<string, JsonElement> Process(IDictionary<string, object> _content)
		{
			Dictionary<string, JsonElement> result = new Dictionary<string, JsonElement>();
			foreach (KeyValuePair<string, object> d in _content)
			{
				result.Add(d.Key, Process(d.Value));
			}
			return result;
		}

		private static JsonElement Process(object _token)
		{
			string typeName = _token.GetType().FullName;
			if (typeName.Contains("Newtonsoft.Json.Linq.JObject"))
			{
				//container
				return new JsonElement(Parse(_token as Newtonsoft.Json.Linq.JObject));
			}
			else if (typeName.Contains("Newtonsoft.Json.Linq.JArray"))
			{
				//array
				return new JsonElement(Parse(_token as Newtonsoft.Json.Linq.JArray));
			}
			else
			{
				//simple value
				return new JsonElement(_token.ToString());
			}
		}
	}
}
